package com.ds.infra.ibatis.executor;

import com.ds.infra.ibatis.mapping.MappedStatement;
import com.ds.infra.ibatis.reflection.MetaObject;
import com.ds.infra.ibatis.handler.DefaultParameterHandler;
import com.ds.infra.ibatis.handler.ParameterHandler;
import com.ds.infra.ibatis.session.Configuration;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 作用：封装了基本的数据库操作，包括查询和更新。
 *
 * @author WeiShaoying
 */
@SuppressWarnings("all")
public abstract class BaseExecutor implements Executor {

    protected final Configuration configuration;

    protected BaseExecutor(Configuration configuration) {
        this.configuration = configuration;
    }

    protected <E> E selectOne(String statement) {
        try (Connection connection = getConnection();
             PreparedStatement ps = prepareStatement(connection, statement);
             ResultSet rs = executeQuery(ps)) {

            if (rs.next()) {
                @SuppressWarnings("unchecked")
                E result = (E) rs.getObject(1);
                return result;
            }
            return null;
        } catch (SQLException e) {
            throw new RuntimeException("Error executing query. Cause: " + e, e);
        }
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter) {
        if (ms == null) {
            throw new RuntimeException("MappedStatement cannot be null");
        }
        try (Connection connection = getConnection();
             PreparedStatement preparedStatement = prepareStatement(connection, ms.getProcessedSql())) {
            if (!ms.getParameterNames().isEmpty() && parameter != null) {
                // 如果有参数，则设置参数值
                ParameterHandler parameterHandler = new DefaultParameterHandler(configuration, parameter, ms);
                parameterHandler.setParameters(preparedStatement);
            }
            // 执行查询操作
            try (ResultSet rs = executeQuery(preparedStatement)) {
                // 将ResultSet转换为对象列表
                return resultSet2Obj(rs, ms.getResultType());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } catch (SQLException e) {
            throw new RuntimeException("Error executing query: " + ms.getId(), e);
        }
    }

    @Override
    public int update(MappedStatement ms, Object parameter) {
        try (Connection connection = getConnection();
             PreparedStatement preparedStatement = prepareStatement(connection, ms.getProcessedSql())) {

            if (!ms.getParameterNames().isEmpty() && parameter != null) {
                // 如果有参数，则设置参数值
                ParameterHandler parameterHandler = new DefaultParameterHandler(configuration, parameter, ms);
                parameterHandler.setParameters(preparedStatement);
            }
            // 执行更新操作
            return preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException("Error updating database. Cause: " + e, e);
        }
    }

    // 检查SQL是否包含参数占位符
    private boolean hasParameters(String sql) {
        return sql.contains("#{") || sql.contains("?");
    }

    protected Connection getConnection() throws SQLException {
        return configuration.getDataSource().getConnection();
    }

    protected PreparedStatement prepareStatement(Connection connection, String sql) throws SQLException {
        return connection.prepareStatement(sql);
    }

    protected ResultSet executeQuery(PreparedStatement ps) throws SQLException {
        return ps.executeQuery();
    }

    // 将ResultSet转换为对象列表
    protected <E> List<E> resultSet2Obj(ResultSet rs, Class<?> returnType) throws Exception {
        List<E> list = new ArrayList<>();
        try {
            while (rs.next()) {
                if (isSimpleType(returnType)) {
                    // 处理基本类型返回值
                    E value = (E) getSimpleResult(rs, returnType, 1);
                    list.add(value);
                } else {
                    // 处理POJO类型
                    E obj = (E) returnType.getDeclaredConstructor().newInstance();
                    MetaObject metaObj = new MetaObject(obj);
                    ResultSetMetaData metaData = rs.getMetaData();
                    for (int i = 1; i <= metaData.getColumnCount(); i++) {
                        String columnName = metaData.getColumnName(i);
                        Object value = rs.getObject(i);
                        metaObj.setValue(columnName, value);
                    }
                    list.add(obj);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Error converting ResultSet to " + returnType.getName(), e);
        }
        return list;
    }

    // 处理基本类型的返回值
    private Object getSimpleResult(ResultSet resultSet, Class<?> returnType, int columnIndex) throws SQLException {
        if (returnType == Long.class || returnType == long.class) {
            return resultSet.getLong(columnIndex);
        } else if (returnType == Integer.class || returnType == int.class) {
            return resultSet.getInt(columnIndex);
        } else if (returnType == String.class) {
            return resultSet.getString(columnIndex);
        } else if (returnType == Boolean.class || returnType == boolean.class) {
            return resultSet.getBoolean(columnIndex);
        } else if (returnType == Double.class || returnType == double.class) {
            return resultSet.getDouble(columnIndex);
        } else if (returnType == Float.class || returnType == float.class) {
            return resultSet.getFloat(columnIndex);
        } else if (returnType == Short.class || returnType == short.class) {
            return resultSet.getShort(columnIndex);
        } else if (returnType == Byte.class || returnType == byte.class) {
            return resultSet.getByte(columnIndex);
        } else if (returnType == Character.class || returnType == char.class) {
            String s = resultSet.getString(columnIndex);
            return s != null && !s.isEmpty() ? s.charAt(0) : null;
        } else {
            return resultSet.getObject(columnIndex, returnType);
        }
    }

    private boolean isSimpleType(Class<?> clazz) {
        return clazz.isPrimitive() ||
                clazz == String.class ||
                Number.class.isAssignableFrom(clazz) ||
                clazz == Boolean.class ||
                clazz == Character.class;
    }

    @Override
    public void close() {
        // Base executor has nothing to close
    }
}
